package at.ac.tuwien.dse.health.rest;

import at.ac.tuwien.dse.health.entity.Account;
import at.ac.tuwien.dse.health.security.SecurityAspect;
import at.ac.tuwien.dse.health.security.SecurityContext;
import at.ac.tuwien.dse.health.security.authentication.AuthenticationManager;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;

/**
 * @author Bernhard Nickel
 * @author Gregor Schauer
 * @author Dominik Strasser
 */
public class RestControllerAspect extends SecurityAspect {
	public static final String HEADER_USERNAME = "username";
	public static final String HEADER_PASSWORD = "password";
	AuthenticationManager authenticationManager;

	@Inject
	public RestControllerAspect(AuthenticationManager authenticationManager) {
		this.authenticationManager = authenticationManager;
	}

	@Override
	public Object doSecuredOperation(ProceedingJoinPoint pjp) throws Throwable {
		initSecurityContext();
		try {
			return super.doSecuredOperation(pjp);
		} finally {
			destroySecurityContext();
		}
	}

	/**
	 * Initializes the {@link SecurityContext} by attempting to authenticate with the provided header parameters
	 * {@link #HEADER_USERNAME} and {@link #HEADER_PASSWORD} if necessary.
	 */
	protected void initSecurityContext() {
		Object role = SecurityContext.getRole();
		if (role == null) {
			ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
			if (attributes == null) {
				return;
			}
			HttpServletRequest request = attributes.getRequest();
			// Check if username and password are set
			String username = request.getHeader(HEADER_USERNAME);
			String password = request.getHeader(HEADER_PASSWORD);

			// If both header parameters are present, attempt to perform an authentication
			if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
				Account account = authenticationManager.authenticate(username, password);
				if (account != null) {
					// If there is an account with the provided credentials, login and continue
					role = account.getUser();
				}
			}
			SecurityContext.setRole(role);
		}
	}

	/**
	 * Resets the {@link SecurityContext}.
	 */
	protected void destroySecurityContext() {
		SecurityContext.setRole(null);
	}
}
